# -*- coding: UTF-8 -*-
from django.conf import settings
from django.core import exceptions
from django.utils import httpwrappers
from django.core.mail import mail_managers
from django.views.core.flatfiles import flat_file
import md5, os
from urllib import urlencode

class CommonMiddleware:
    """
    Le module intermédiaire "de base", qui réalise quelques opérations simples :

        - Interdiction d'accès aux requêtes dont l'en-tête User-Agent est présent dans
          settings.DISALLOWED_USER_AGENTS

        - Réécriture d'URL : ce module intermédiaire ajoutera à la fin de l'URL
          demandée un slash (si ce dernier n'est pas déjà présent) et il ajoutera
          au début de l'URL demandée le préfixe "www." (si celui-ci n'est pas
          déjà présent), en fonction des paramètres APPEND_SLASH et PREPEND_WWW,
          respectivement

        - En-tête ETag : si le paramètre USE_ETAGS est activé, pour chaque réponse
          un en-tête ETag est généré à partir de la totalité du contenu de la page
          et, le cas échéant, une réponse "Not Modified" est renvoyée

        - Fichiers bruts : dans le cas d'une réponse 404, ce module intermédiaire va
          chercher un fichier brut correspondant au chemin d'accès demandé par la
          requête; si un tel fichier existe, il sera utilisé

    Nous vous conseillons de faire de l'objet CommonMiddleware la première entrée
    de votre paramètre MIDDLEWARE_CLASSES;
    """

    def process_request(self, request):
        """
        Vérifie si l'en-tête User-Agent n'est pas interdit et réécrit l'URL
        demandée par la requête en fonction des paramètres
        settings.APPEND_SLASH et settings.PREPEND_WWW.
        """

        # On vérifie si l'en-tête User-Agent n'est pas interdit
        if request.META.has_key('HTTP_USER_AGENT'):
            for user_agent_regex in settings.DISALLOWED_USER_AGENTS:
                if user_agent_regex.search(request.META['HTTP_USER_AGENT']):
                    return httpwrappers.HttpResponseForbidden('<h1>Accès interdit</h1>')

        # On vérifie si une redirection est nécessaire, en fonction des paramètres settings.APPEND_SLASH et settings.PREPEND_WWW
        old_url = [request.META['HTTP_HOST'], request.path]
        new_url = old_url[:]
        if settings.PREPEND_WWW and not old_url[0].startswith('www.'):
            new_url[0] = 'www.' + old_url[0]
        # On ajoute un slash à la fin de l'URL si le paramètre append_slash est
        # activé et si l'URL n'a pas déjà un slash final ou ne se termine pas par
        # une extension de fichier.
        if settings.APPEND_SLASH and (old_url[1][-1] != '/') and ('.' not in old_url[1].split('/')[-1]):
            new_url[1] = new_url[1] + '/'
        if new_url != old_url:
            # On redirige le client
            newurl = "%s://%s%s" % (os.environ.get('HTTPS') == 'on' and 'https' or 'http', new_url[0], new_url[1])
            if request.GET:
                newurl += '?' + urlencode(request.GET)
            return httpwrappers.HttpResponseRedirect(newurl)

        return None

    def process_response(self, request, response):
        """
        Cherche un fichier brut (en cas de réponse 404) et génère si nécessaire un en-tête Etag.
        """

        # Si la réponse est une réponse 404, on regarde s'il n'y a pas un fichier brut correspondant au chemin d'accès demandé par la requête
        if response.status_code == 404:
            try:
                response = flat_file(request, request.path)
            except exceptions.Http404:
                # Si la requête provenait d'un lien interne (on détermine si c'est le cas
                # grâce à l'en-tête HTTP_REFERER) ou d'un site qui n'était pas un moteur de
                # recherche, on envoie une note aux gérants du site.
                if settings.SEND_BROKEN_LINK_EMAILS:
                    domain = request.META['HTTP_HOST']
                    referer = request.META.get('HTTP_REFERER', None)
                    is_internal = referer and (domain in referer)
                    path = request.get_full_path()
                    if referer and not _is_ignorable_404(path) and (is_internal or '?' not in referer):
                        mail_managers("Lien%s mort sur le site %s" % ((is_internal and ' INTERNE' or ''), domain),
                            "HTTP_REFERER : %s\nURL demandée : %s\n" % (referer, request.get_full_path()))
                # Si on n'a trouvé aucun fichier brut il vaut mieux renvoyer la réponse 404 d'origine
                return response

        # On génère l'en-tête ETag si on nous l'a demandé
        if settings.USE_ETAGS:
            etag = md5.new(response.get_content_as_string('utf-8')).hexdigest()
            if request.META.get('HTTP_IF_NONE_MATCH') == etag:
                response = httpwrappers.HttpResponseNotModified()
            else:
                response['ETag'] = etag

        return response

def _is_ignorable_404(uri):
    "Renvoie True si une erreur 404 liée à l'URL spécifiée *ne doit pas* être signalée aux gérants du site"
    for start in settings.IGNORABLE_404_STARTS:
        if uri.startswith(start):
            return True
    for end in settings.IGNORABLE_404_ENDS:
        if uri.endswith(end):
            return True
    if '_files' in uri:
        # L'URI est probablement émise à partir d'une copie hors ligne de la page.
        return True
    return False
